package jwt

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"git.myscrm.cn/golang/common/uuid"
	"git.myscrm.cn/golang/stark/v4"
	"github.com/dgrijalva/jwt-go"
	"time"
)

// 一些常量
var (
	TokenExpired     = errors.New("token is expired")
	TokenNotValidYet = errors.New("token not active yet")
	TokenMalformed   = errors.New("that's not even a token")
	TokenInvalid     = errors.New("couldn't handle this token")
)

const DEFAULT_SINGKEY = "7264daa3-0cdb-11ed-8246-043f72e4f15a"

type JWT struct {
	SigningKey []byte
}

var DefaultJWT = &JWT{[]byte(DEFAULT_SINGKEY)}

type GfyxClaims struct {
	//租户code
	T string `json:"t"`
	//用户编码
	U string `json:"u"`
	//应用标志
	A string `json:"a"`
	jwt.StandardClaims
}

// MainAppClaims 主应用的jwt编码字段
type MainAppClaims struct {
	//租户code
	T string `json:"t"`
	//用户编码
	U string `json:"u"`
	//应用标志
	A string `json:"a"`
	jwt.StandardClaims
}

// CreateToken
// 创建token
func (j *JWT) CreateToken(claims GfyxClaims) (string, error) {
	token := jwt.New(jwt.SigningMethodHS256)
	token.Claims = claims
	res, err := token.SignedString(j.SigningKey)
	return res, err
}

// ParseToken
// 解析token
func (j *JWT) ParseToken(tokenString string) (*GfyxClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &GfyxClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(*GfyxClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, TokenInvalid
}

// RefreshToken
// 更新token
func (j *JWT) RefreshToken(tokenString string) (string, error) {
	jwt.TimeFunc = func() time.Time {
		return time.Unix(0, 0)
	}

	token, err := jwt.ParseWithClaims(tokenString, &GfyxClaims{}, func(token *jwt.Token) (interface{}, error) {
		return j.SigningKey, nil
	})

	if err != nil {
		return "", err
	}

	if claims, ok := token.Claims.(*GfyxClaims); ok && token.Valid {
		jwt.TimeFunc = time.Now
		claims.StandardClaims.ExpiresAt = time.Now().Add(1 * time.Hour).Unix()
		return j.CreateToken(*claims)
	}

	return "", TokenInvalid
}

// GenerateGfyxToken 生成建模的jwt token
func (j *JWT) GenerateGfyxToken(tenantCode, tenantUserCode string) (string, error) {
	var gfyxSignKey = stark.Configer.(stark.ApolloConfigerIface).GetStringValue("gfyx_model_main_key", "")
	var gfyxAppId = stark.Configer.(stark.ApolloConfigerIface).GetStringValue("gfyx_model_main_appid", "")

	j.SigningKey = []byte(gfyxSignKey)
	claims := GfyxClaims{
		tenantCode,
		tenantUserCode,
		gfyxAppId,
		jwt.StandardClaims{
			NotBefore: time.Now().Unix() - 1000,
			IssuedAt:  time.Now().Unix(),
			ExpiresAt: time.Now().Unix() + 3600,
			Issuer:    "ycg-gfyx",
			Id:        uuid.GetUUID(),
		},
	}

	token, err := j.CreateToken(claims)
	return token, err
}

// GenerateMainAppToken 生成主应用的token(第三方角度来创建的)（测试用）
func (j *JWT) GenerateMainAppToken(tenantCode, tenantUserCode string) (string, error) {
	//vars gfyxSignKey = stark.Configer.GetStringValue("gfyx_model_main_key", "")
	gfyxSignKey := DEFAULT_SINGKEY
	var gfyxAppId = stark.Configer.(stark.ApolloConfigerIface).GetStringValue("gfyx_model_main_appid", "")

	j.SigningKey = []byte(gfyxSignKey)
	claims := MainAppClaims{
		tenantCode,
		tenantUserCode,
		gfyxAppId,
		jwt.StandardClaims{
			NotBefore: time.Now().Unix() - 1000,
			IssuedAt:  time.Now().Unix(),
			ExpiresAt: time.Now().Unix() + 3600,
			Issuer:    "ycg-gfyx",
			Id:        uuid.GetUUID(),
		},
	}
	//token := jwt.New(jwt.SigningMethodHS256)
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	//token.Claims = claims
	tokenString, err := token.SignedString(j.SigningKey)
	return tokenString, err
}

// ParseMainAppToken 解析主应用token
func (j *JWT) ParseMainAppToken(tokenString string, signingKey string) (*MainAppClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &MainAppClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(signingKey), nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(*MainAppClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, TokenInvalid
}

// HmacSha256
// hmacsha256验证
func HmacSha256(src, key string) (string, error) {
	m := hmac.New(sha256.New, []byte(key))
	_, err := m.Write([]byte(src))
	if err != nil {
		return "", err
	}

	return hex.EncodeToString(m.Sum(nil)), nil
}
